﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Metadata.SPI;

namespace T.FAS.Runtime.Metadata.Dao
{
    public sealed class RefSchemaService
    {
        public static RefSchemaService s_service = new RefSchemaService();
        public static RefSchemaService Instance { get { return s_service; } }
        public static string commonJSPrefix = "localMessage[\"fas_common\"] = ";
        public static string pageJSPrefix = "localMessage[\"{0}\"] = ";

        private static readonly Dictionary<string, IMetaDependencyHandler> s_cache = new Dictionary<string, IMetaDependencyHandler>();
        private readonly Type _refSchemaType = typeof(RefSchemaTopLayer);
        private readonly Type _schemaColle = typeof(RefSchemaEntityColle);
        private List<MetaDataTypeInfo> _metaDataTypeInfos;

        public List<MetaDataTypeInfo> MetaDataTypeInfos
        {
            get
            {
                if (_metaDataTypeInfos == null)
                {
                    _metaDataTypeInfos = new MetaDataTypeInfoDao().GetMetaDataTypeInfos();
                }
                return _metaDataTypeInfos;
            }
        }

        private RefSchemaService() { }

        /// <summary>
        /// 获取拉平的结构，key为DO_User还是Schema.DO.DO_User
        /// </summary>
        /// <param name="refSchema"></param>
        /// <returns></returns>
        public List<MetaDataRefFlat> GetMetaDataFlatValue(RefSchema refSchema)
        {
            if (refSchema == null) return default;
            List<MetaDataRefFlat> result = new List<MetaDataRefFlat>();
            RefSchemaTopLayer top = refSchema.Schema;
            List<PropertyInfo> schemaColleProps = _refSchemaType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).ToList();
            schemaColleProps = schemaColleProps.Where(p => p.PropertyType == _schemaColle).ToList();

            foreach (PropertyInfo prop in schemaColleProps)
            {
                RefSchemaEntityColle schemaEntityColle = prop.GetValue(top) as RefSchemaEntityColle;
                if (schemaEntityColle != null)
                {
                    foreach (var key in schemaEntityColle.Keys)
                    {
                        if (schemaEntityColle[key] != null)
                        {
                            var schemaKey = $"{RefSchemaConst.Schema}.{prop.Name}.{key}";
                            result.Add(new MetaDataRefFlat
                            {
                                MetaDataID = schemaEntityColle[key].ID,
                                MetaDataCode = schemaEntityColle[key].Code,
                                SchemaKey = schemaKey,
                                SchemaValue = JsonConvert.SerializeObject(schemaEntityColle[key]),
                            });
                        }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 从拉平的结构还原，key:Schema.DO.DO_User
        /// </summary>
        /// <param name="refSchema"></param>
        /// <returns></returns>
        public RefSchema RecoverRefSchemaFromFlatValue(List<MetaDataRefFlat> metaDataSchemas)
        {
            try
            {
                if (metaDataSchemas == null || metaDataSchemas.Count == 0) return default;
                RefSchema refSchema = new RefSchema
                {
                    Schema = new RefSchemaTopLayer()
                };
                foreach (var metaInfo in metaDataSchemas)
                {
                    SetIntoRefSchema(refSchema, metaInfo);
                }
                return refSchema;
            }
            catch (Exception e)
            {
                throw new Exception($"还原RefSchema失败, err:{e.Message}\r\n{e}\r\nContent:{JsonConvert.SerializeObject(metaDataSchemas)}", e);
            }
            
            //JObject refSchemaObj = new JObject();
            //JObject schemaObj = new JObject();
            //refSchemaObj[RefSchemaConst.Schema] = schemaObj;
            //foreach (var key in metaDataSchemaDict.Keys)
            //{
            //    var metaType = key.GetStrFromSpecifierCharOccurIndex('.', 1, 1);
            //    var metaDataCode = key.GetStrFromSpecifierCharOccurIndex('.', 2, 1);
            //    if (!(schemaObj as IDictionary<string, JToken>).ContainsKey(metaType))
            //    {
            //        refSchemaObj[RefSchemaConst.Schema][metaType] = new JObject();
            //    }
            //    refSchemaObj[RefSchemaConst.Schema][metaType][metaDataCode] = JObject.Parse(metaDataSchemaDict[key]);
            //}
            //var schema = refSchemaObj.ToObject(new JsonSerializer);
        }

        /// <summary>
        /// 将翻译后词条Merge到RefSchema中
        /// </summary>
        /// <param name="refSchema"></param>
        /// <param name="translatedTerms"></param>
        /// <returns></returns>
        public Dictionary<string, object> GetMetaDataPropertyFlatValue(RefSchema refSchema)
        {
            if (refSchema == null) return default;
            Dictionary<string, object> result = new Dictionary<string, object>();
            var jObj = JObject.FromObject(refSchema);
            var leafTokens = GetAllLeafNodeToken(jObj);
            if (leafTokens != null && leafTokens.Count > 0)
            {
                foreach (var prop in leafTokens)
                {
                    result[prop.Path] = prop.Value;
                }
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="translatedTerms"></param>
        /// <returns></returns>
        public string RecoverRefSchemaFromFlatPropertyValue(Dictionary<string, string> translatedTerms)
        {
            var rootObj = new JObject();
            var schemaObj = new JObject();
            rootObj[RefSchemaConst.Schema] = schemaObj;

            foreach (var item in translatedTerms)
            {
                SetRefExprIntoSchemaObj(schemaObj, item);
            }
            return rootObj.ToString();
        }

        private static void SetRefExprIntoSchemaObj(JObject schemaObj, KeyValuePair<string, string> item)
        {
            var schemaPathNodes = item.Key.Split('.');
            if (schemaPathNodes != null && schemaPathNodes.Length > 0)
            {
                JObject curObj = schemaObj; //Schema
                for (int i = 1; i < schemaPathNodes.Length; i++)
                {
                    var pathNodes = schemaPathNodes[i]; //DQ
                    var tmp = curObj as IDictionary<string, JToken>;
                    if (tmp == null)
                        continue;
                    if (i == schemaPathNodes.Length - 1) //当最后一个的时候，赋值属性
                    {
                        tmp[pathNodes] = new JValue(item.Value);
                    }
                    else
                    {
                        if (!tmp.ContainsKey(pathNodes))
                        {
                            tmp[pathNodes] = new JObject();
                        }
                        else
                        {
                            if (tmp[pathNodes] == null)
                            {
                                tmp[pathNodes] = new JObject();
                            }
                            else if (tmp[pathNodes] is JValue val && val.Value == null)
                            {
                                tmp[pathNodes] = new JObject();
                            }
                        }
                        curObj = tmp[pathNodes] as JObject;
                    }
                }
            }
        }

        /// <summary>
        ///  发布时-将翻译后词条Merge到RefSchema中
        /// </summary>
        /// <param name="refSchema"></param>
        /// <param name="translatedTerms"></param>
        /// <returns></returns>
        public string MergeWithTranslatedTerms(RefSchema refSchema, Dictionary<string, string> translatedTerms)
        {
            if (refSchema == null || translatedTerms == null) return default;
            JObject rootObj = JObject.FromObject(refSchema);
            JObject schemaObj = rootObj.GetValue(RefSchemaConst.Schema) as JObject;
            foreach (var terms in translatedTerms)
            {
                if (!string.IsNullOrEmpty(terms.Key) && terms.Key.StartsWith($"{RefSchemaConst.Schema}."))
                {
                    try
                    {
                        var jVal = rootObj.SelectToken(terms.Key) as JValue;
                        if (jVal != null)
                        {
                            jVal.Value = terms.Value;
                        }
                        else
                        {
                            SetRefExprIntoSchemaObj(schemaObj, terms);
                        }
                    }
                    catch (Exception e)
                    {
                        throw new Exception($"将翻译词条merge到RefSchema失败，msg:{e.Message}，termKey:{terms.Key}，rootObj:{rootObj}，\r\ne:{e.StackTrace}", e);
                    }
                }
            }
            return rootObj.ToString();
        }

        public string GenerateJSContent(Dictionary<string, string> translatedTerms, List<MetaDataRefFlat> metaDataSchemas = null,  string pageCode = "$Common")
        {
            string refSchemaJsContent;

            //page依赖其他元数据
            if (metaDataSchemas != null && metaDataSchemas.Count > 0)
            {
                RefSchema refSchema = RecoverRefSchemaFromFlatValue(metaDataSchemas);
                string refSchemaJson;
                //翻译词条不为空
                if (translatedTerms != null && translatedTerms.Count > 0)
                {
                    refSchemaJson = MergeWithTranslatedTerms(refSchema, translatedTerms);
                }
                else
                {
                    refSchemaJson = JsonConvert.SerializeObject(refSchema);
                }
                refSchemaJsContent = $"{string.Format(pageJSPrefix, pageCode)}{refSchemaJson}";
            }
            else
            {
                //翻译词条不为空
                if (translatedTerms == null || translatedTerms.Count == 0)
                {
                    refSchemaJsContent = pageCode == "$Common"
                                                ? $"{commonJSPrefix}{{}}"
                                                : $"{string.Format(pageJSPrefix, pageCode)}{{}}";
                }
                else
                {
                    var content = RecoverRefSchemaFromFlatPropertyValue(translatedTerms);
                    refSchemaJsContent = pageCode == "$Common"
                                                ? $"{commonJSPrefix}{content}"
                                                : $"{string.Format(pageJSPrefix, pageCode)}{content}";
                }
            }
            //refSchemaJsContent = GetUTF8JsContent(refSchemaJsContent);
            refSchemaJsContent = refSchemaJsContent.ConvertToUTF8();
            return refSchemaJsContent;
        }

        private static string GetUTF8JsContent(string refSchemaJsContent)
        {
            if (string.IsNullOrEmpty(refSchemaJsContent)) return refSchemaJsContent;
            Encoding encoding = refSchemaJsContent.GetEncoding();

            try
            {
                // 如果字符集不是UTF8，则将字符串转换为UTF8编码
                if (encoding != Encoding.UTF8)
                {
                    byte[] originalBytes = encoding.GetBytes(refSchemaJsContent);
                    string utf8String = Encoding.UTF8.GetString(originalBytes);
                    return utf8String;
                }
                else
                {
                    return refSchemaJsContent;
                }
            }
            catch (Exception e)
            {
                return refSchemaJsContent;
            }
        }

        #region Private Funcs

        private void SetIntoRefSchema(RefSchema refSchema, MetaDataRefFlat metaInfo)
        {
            if (refSchema == null) return;
            var metaTypeAbbr = metaInfo.SchemaKey.GetStrFromSpecifierCharOccurIndex('.', 1, 1);
            var metaDataCode = metaInfo.SchemaKey.GetStrFromSpecifierCharOccurIndex('.', 2, 1);

            var prop = _refSchemaType.GetProperty(metaTypeAbbr);
            RefSchemaEntityColle propVal = prop.GetValue(refSchema.Schema) as RefSchemaEntityColle;
            if (propVal == null)
            {
                propVal = new RefSchemaEntityColle();
                prop.SetValue(refSchema.Schema, propVal);
            }
            propVal[metaDataCode] = GetRefSchemaEntity(metaTypeAbbr, metaInfo.SchemaValue);
        }

        private IRefSchemaEntity GetRefSchemaEntity(string metaDataTypeAbbr, string json)
        {
            try
            {
                var metaTypeID = MetaDataTypeInfos.FirstOrDefault(typeinfo => typeinfo.Code == metaDataTypeAbbr)?.ID;
                var handler = GetDependencyHandler(metaTypeID);
                return handler.DeserializeRefSchemaEntity(json);
            }
            catch (Exception e)
            {
                throw new Exception($"获取元数据类型:{metaDataTypeAbbr},SchemaEntity反序列化失败。err:{e.Message}\r\n{e}\r\njson:\r\n{json}", e);
            }
        }

        /// <summary>
        /// 获取所有叶子属性
        /// </summary>
        /// <param name="tokens"></param>
        /// <returns></returns>
        private List<JProperty> GetAllLeafNodeToken(IEnumerable<JToken> tokens)
        {
            List<JProperty> leafTokens = new List<JProperty>();
            if (tokens == null) return leafTokens;
            foreach (var token in tokens)
            {
                if (token.Type == JTokenType.Property)
                {
                    JProperty prop = (JProperty)token;
                    if (prop.Value is JValue jVal && jVal.Value != null)
                        leafTokens.Add(prop);
                }
                if (token.HasValues)
                {
                    leafTokens.AddRange(GetAllLeafNodeToken(token.Children()));
                }
            }
            return leafTokens;
        }

        private static IMetaDependencyHandler GetDependencyHandler(string metaTypeID)
        {
            if (string.IsNullOrEmpty(metaTypeID)) return null;
            if (s_cache.ContainsKey(metaTypeID)) return s_cache[metaTypeID];
            else
            {
                try
                {
                    s_cache[metaTypeID] = MetaDataFactory.CreateMetaDependencyHandler(metaTypeID);
                }
                catch (Exception e)
                {
                    throw new Exception($"获取依赖器失败, metaTypeID:{metaTypeID}, err:{e.Message}\r\n{e}", e);
                }
                if (s_cache[metaTypeID] == null)
                    throw new Exception($"获取依赖器失败, metaTypeID:{metaTypeID}");
                return s_cache[metaTypeID];
            }
        }

        #endregion

        public class MetaDataRefFlat
        {
            public string MetaDataID { get; set; }
            public string MetaDataCode { get; set; }
            public string SchemaKey { get; set; }
            public string SchemaValue { get; set; }
        }
    }

    public sealed class RefSchemaConst
    {
        public const string Schema = "Schema";
    }
}
